//
//  Emoticon.swift
//  CZEmoticon
//
//  Created by Apple on 15/12/4.
//  Copyright © 2015年 itcast. All rights reserved.
//

import UIKit

/// 表情包模型
class EmoticonPackage: NSObject {
    
    // 表情包对应的文件夹名称
    var id: String?
    
    // 表情包名称
    var group_name_cn: String?
    
    // 表情模型数组
    var emoticons = [Emoticon]()
    
    /// 字典转模型
    init(id: String) {
        self.id = id
        super.init()
    }
    
    // 字典的key在模型中找不到对应的属性, 特别注意 不要写错方法
    override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
    
    override var description: String {
        
        return "\n\t: 表情包模型: id: \(id), group_name_cn: \(group_name_cn), emoticons: \(emoticons)"
    }
    
    // 获取Emoticons.bundle的路径
    private static let bundlePath = NSBundle.mainBundle().pathForResource("Emoticons", ofType: "bundle")!
    
    /// 静态属性,保存表情包模型数据, 只会加载一次
    static let packages = EmoticonPackage.loadPackages()
    
    // 加载表情包
    private class func loadPackages() -> [EmoticonPackage] {
        print("加载表情包")
        
        // 获取 emoticons.plist的路径 = bundlePath + "/emoticons.plist"
        let plistPath =  (bundlePath as NSString).stringByAppendingPathComponent("emoticons.plist")
        
        // 获取 emoticons.plist 内容
        let emoticonsDict = NSDictionary(contentsOfFile: plistPath)!
//        print("emoticonsDict: \(emoticonsDict)")
        
        // 获取表情包文件夹名称 [Int] [[String: AnyObject]]: 数组里面的元素是字典
        let packageArr = emoticonsDict["packages"] as! [[String: AnyObject]]
        
        // 存放表情包模型的数组
        var packages = [EmoticonPackage]()
        
        // 创建 `最近` 表情包
        let recentPackage = EmoticonPackage(id: "")
        
        // 设置表情包名称
        recentPackage.group_name_cn = "最近"
        
        // 追加20个空表情和一个删除按钮
        recentPackage.appendEmptyEmoticon()
        
        // 添加到表情包
        packages.append(recentPackage)
        
        // 从Emoticons.bunble里面加载出来的
        // 遍历获取每一个表情包文件名称
        for dict in packageArr {
            // 获取id
            let id = dict["id"] as! String
            
            // 创建表情包
            let package = EmoticonPackage(id: id)
            
            // 根据当前表情包模型,加载表情包名称和所有的表情模型
            package.loadEmoticon()
            
            packages.append(package)
        }
        
        // 返回加载到的表情包模型
        return packages
    }
    
    /// 根据当前表情包模型,加载表情包名称和所有的表情模型
    func loadEmoticon() {
        // 获取对应表情包模型里面的info.plist = bundlePath + 表情包文件夹名称 + info.plist
        let infoPlist = EmoticonPackage.bundlePath + "/\(id!)" + "/info.plist"
        
        // 把infoPlist的内容加载到内存
        let infoDict = NSDictionary(contentsOfFile: infoPlist)!
//        print("infoDict: \(infoDict)")
        
        // 获取到表情包名称
        group_name_cn = infoDict["group_name_cn"] as! String
        
        // 获取表情包里面的所有表情模型
        let emoticonArr = infoDict["emoticons"] as! [[String: AnyObject]]
        
        // 记录
        var index = 0
        
        // 遍历 emoticonArr 数组,生成表情模型
        for dict in emoticonArr {
            // 字典转模型创建表情的时候,把表情包文件夹名称也传入进来
            let emoticon = Emoticon(id: id!, dict: dict)
            emoticons.append(emoticon)
            
            index++
            
            // 判断,如果是每一页的最后一个,添加一个删除按钮
            if index == 20 { // 一页的最后一个
                // 创建删除按钮
                let removeEmoticon = Emoticon(removeEmoticon: true)
                
                // 添加到表情模型数组
                emoticons.append(removeEmoticon)
                
                // 清空index
                index = 0
            }
        }
        
        // 表情包加载完成,来判断这个表情包最后一页,是否够21格按钮.不够的话,填充空白按钮,并在最后添加一个删除按钮
        appendEmptyEmoticon()
    }
    
    /// 填充空白按钮,并在最后添加一个删除按钮
    func appendEmptyEmoticon() {
        // 判断,这个表情包最后一页有多少个按钮
        let count = emoticons.count % 21    // count == 0 正好满一页
        
        // 当是 `最近` 表情包时, emoticons.count 数量是等于0, count也等于0,进入不了追加表情,从bundle加载的表情 emoticons.count不等于0
        // emoticons.count == 0表示`最近` 表情包
        if count > 0 || emoticons.count == 0 {  // 表示最后一页没有满21个,只有count个
            // 追加多少个空白按钮
            // count == 1, 需要追加19(20 - 1)(count..<20)个空白按钮和一个删除按钮
            // count == 2, 需要追加18(20 - 2)(count..<20)个空白按钮和一个删除按钮
            // 追加按钮
            for _ in count..<20 {
                // 创建空白按钮模型
                let emptyEmoticon = Emoticon(removeEmoticon: false)
                
                // 添加到表情模型数组
                emoticons.append(emptyEmoticon)
            }
            
            // 追加一个删除按钮
            emoticons.append(Emoticon(removeEmoticon: true))
        }
    }
    
    /*
        最近表情包,永远都只有21个表情,而且最后一个是删除按钮,表情需要按使用次数,多的排在前面
    */
    
    /**
    将表情添加到 `最近` 表情包
    
    - parameter emoticon: 要添加的表情模型
    */
    class func addFavorate(emoticon: Emoticon) {
        // 如果是删除按钮不需要添加
        if emoticon.removeEmoticon {
            return
        }
        
        // 如果是空白按钮不需要添加, 空白按钮即没有图片,也没有emoji
        if emoticon.pngPath == nil && emoticon.emoji == nil {
            return
        }
        
        // 使用次数加1
        emoticon.times++
        
        // 获取最近表情包所有表情模型
        // 数组是值传递,不像OC里面使用的是指针
        var recentEmoticons = packages[0].emoticons
        
        // 删除按钮不需要排序,先讲删除从数组中移除,在排完序之后,在添加回去
        let removeEmoticon = recentEmoticons.removeLast()   // removeLast会将移除的元素作为返回值
        
        // 如果 `最近` 表情包 已经包含这个模型 不需要再添加
        let contains = recentEmoticons.contains(emoticon)
        
        // 不再表情包里面,才需要添加
        if !contains {
            // 添加表情模型
            recentEmoticons.append(emoticon)
        }
        
        // 使用次数多得排在前面,对数组里面的所有元素排序,需要将排序后的数组重新赋值给 recentEmoticons
        recentEmoticons = recentEmoticons.sort { (e1, e2) -> Bool in
            // 使用次数多得排在前面
            return e1.times > e2.times
        }
        
        // 如果前面有往最近表情模型数组添加表情模型,为了保证永远只有21个,需要删除,排序后数组最后一个模型
        if !contains {
            // 移除最后一个
            recentEmoticons.removeLast()
        }
        
        // 将删除按钮添加回去
        recentEmoticons.append(removeEmoticon)

        // 赋值回去,千万要记得赋值回去
        packages[0].emoticons = recentEmoticons
    }
}

/// 表情模型
class Emoticon: NSObject {
    
    // 表情包文件夹名称
    var id: String?
    
    // 表情传输名称
    var chs: String?
    
    // 表情对应的图片
    var png: String? {
        didSet {
            // 计算图片的完整路径 = bundlePath + 表情包文件夹名称 + 图片名称
            pngPath = EmoticonPackage.bundlePath + "/\(id!)" + "/\(png!)"
        }
    }
    
    // 图片的完整路径
    var pngPath: String?
    
    // code emoji的16进制字符串
    var code: String? {
        didSet {
            // 扫描
            let scanner = NSScanner(string: code!)
            
            // result: UnsafeMutablePointer<UInt32>: UInt32指针
            var result: UInt32 = 0
            
            // 将结果赋值给result
            scanner.scanHexInt(&result)
            
            let char = Character(UnicodeScalar(result))
            
            // 将code转成emoji表情
            emoji = "\(char)"
        }
    }
    
    // emoji表情
    var emoji: String?
    
    // 使用次数
    var times = 0
    
    // false表示空表情, true表示删除按钮,
    // 空表情和删除按钮都不需要其他属性
    var removeEmoticon = false
    init(removeEmoticon: Bool) {
        self.removeEmoticon = removeEmoticon
        super.init()
    }
    
    /// 字典转模型, 要么显示图片表情,要么显示Emoji表情
    init(id: String, dict: [String: AnyObject]) {
        self.id = id
        super.init()
        
        // KVC字典转模型
        setValuesForKeysWithDictionary(dict)
    }
    
    // 字典的key在模型中找不到对应的属性, 特别注意 不要写错方法
    override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
    
    override var description: String {
        return "\n\t\t: 表情模型: chs: \(chs), png: \(png), code: \(code), times: \(times), removeEmoticon: \(removeEmoticon)"
    }
    
    /**
    根据表情模型创建带表情图片的属性文本
    
    - parameter font: font
    
    - returns: 带表情图片的属性文本
    */
    func emoticonToAttrString(font: UIFont) -> NSAttributedString {
        // 判断是否有图片
        guard let pngPath = self.pngPath else {
            print("pngPath为空")
            return NSAttributedString(string: "")
        }
        
        // 属性文本可以添加图片
        // 创建附件
        let attachment = EmoticonTextAttachment()
        
        let width = font.lineHeight
        
        // 设置附件的大小
        attachment.bounds = CGRect(x: 0, y: -4, width: width, height: width)
        
        // 给附件添加图片
        attachment.image = UIImage(contentsOfFile: pngPath)
        
        // 将表情名称赋值给attachment附件
        attachment.name = self.chs
        
        // 创建一个属性文本,属性文本有一个附件,附件里面有一张图片
        let attrString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment))
        
        // 附件没有Font属性,会变小
        // 给附件添加Font属性
        attrString.addAttribute(NSFontAttributeName, value: font, range: NSRange(location: 0, length: 1))
        
        return attrString
    }
    
    /**
    根据表情名称获取对应的表情模型
    
    - parameter string: 表情模型
    */
    class func stringToEmoticon(chs: String) -> Emoticon? {
        // 根据表情名称获取对应的表情模型
        // 判断 chs 传入的表情名称是否等于模型里面的chs
        
        // 找到的表情模型
        var emoticon: Emoticon? = nil
        
        // 表情包 -> 表情模型
        // 遍历表情包里面的所有表情模型
        for package in EmoticonPackage.packages {
            // 获得一个表情包里面所有表情模型
            
            // filter: 遍历每一个模型
            let findEmoticons = package.emoticons.filter({ (e1) -> Bool in
                // 判断 模型的chs 是否等于 传入的chs.等于就表示找到这个模型
                return e1.chs == chs    // true表示匹配成功
            })
            
            // findEmoticons 匹配的结果,放在数组里面.获取数组里面的第一元素赋值给emoticon
            emoticon = findEmoticons.first
            
            if emoticon != nil {
                // 找到了表情模型
                break
            }
        }
        
        return emoticon
    }
    
    /**
    将带表情字符串的微博内容转成带表情图片的微博内容
    */
    class func stringToEmoticonString(text: String) -> NSAttributedString {
        // 1.从微博内容中截取表情文本
        // [ ] 在正则表达式里面是有特殊含义的,需要转义
        let regular = try! NSRegularExpression(pattern: "\\[.*?\\]", options: NSRegularExpressionOptions.DotMatchesLineSeparators)
        
        // 匹配
        let results = regular.matchesInString(text, options: NSMatchingOptions(rawValue: 0), range: NSRange(location: 0, length: text.characters.count))
        
        // 将传入的内容转成属性文本(普通的文本是不能将属性文本替换进去)
        let attrM = NSMutableAttributedString(string: text)
        
        // 匹配结果数量
        let count = results.count
        
        // 反过来替换内容,从匹配结果数组后面往前面替换
        for var i = count - 1; i >= 0; i-- {
            let result = results[i]
            //            print("每个结果匹配到几个: \(result.numberOfRanges)")
            let range = result.rangeAtIndex(0)
            // 截取表情名称
            let emoticonString = (text as NSString).substringWithRange(range)
//            print("匹配到的结果: \(emoticonString)")
            
            // 根据表情名称获取对应的表情模型
            if let emoticon = Emoticon.stringToEmoticon(emoticonString) {
                // 找到的表情模型
                // attrString 带表情图片的属性字符串
                let attrString = emoticon.emoticonToAttrString(UIFont.systemFontOfSize(14))
                
                "我[爱你],[笑哈哈]"
                attrM.replaceCharactersInRange(range, withAttributedString: attrString)
            }
        }
        
        return attrM
    }
}
